#include "SerialUSB.h"
#include <sys/_stdint.h>
#include "Radio.h"
//#include <patch_init.h>
#include <patch_ssb_compressed.h>

//#define I2S_Radio

#if defined(ARDUINO_RASPBERRY_PI_PICO_W)  //Raspberry pi pico w

#define RESET_PIN 2 //16
#define I2C_SDA 0  //18 //0
#define I2C_SCL 1  //19 //1

#elif defined(ESP8266)  // ESP8266

#define RESET_PIN 16  // (GPIO16)
#define I2C_SDA 4     // (GPIO04)
#define I2C_SCL       // (GPIO05)

#elif defined(__AIR001xx_HAL_CONF_DEFAULT_H)  // Air001

#define RESET_PIN PB1  // (GPIO14)
#define I2C_SDA PB4    // (GPIO04)
#define I2C_SCL PB5    // (GPIO05)

#else
#define RESET_PIN 16
#define I2C_SDA 0  //18 //0
#define I2C_SCL 1  //19 //1
#endif

#define AM_FUNCTION 1
#define FM_FUNCTION 0

#define PULLING_RDS 40


Radio::Radio() {
  _rx = new SI4735();

  digitalWrite(RESET_PIN, HIGH);
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
  Wire.setSCL(I2C_SCL);
  Wire.setSDA(I2C_SDA);
  Wire.begin();
#elif defined(ESP8266)
  Wire.begin(I2C_SDA, I2C_SCL);
#endif

  // rx.setDeviceOtherI2CAddress(0x11);
  int16_t si4735Addr = _rx->getDeviceI2CAddress(RESET_PIN);

  if (si4735Addr == 0) {
    Serial.println("Si473X not found!");
    Serial.flush();
    while (1)
      ;
  } else {
    Serial.print("The Si473X I2C address is 0x");
    Serial.println(si4735Addr, HEX);
  }
  delay(500);
  // rx.setup(RESET_PIN, -1, FM_CURRENT_MODE, SI473X_ANALOG_DIGITAL_AUDIO);
  //_rx->setAudioMuteMcuPin(AUDIO_MUTE); //控制外部电路静音,没有实验
  //_rx->radioPowerUp();
#if defined(I2S_Radio)
  _rx->setup(RESET_PIN, -1, FM_CURRENT_MODE, SI473X_DIGITAL_AUDIO1, XOSCEN_RCLK); 
  _rx->digitalOutputSampleRate(48000);
// OSIZE Dgital Output Audio Sample Precision (0=16 bits, 1=20 bits, 2=24 bits, 3=8bits).
  // OMONO Digital Output Mono Mode (0=Use mono/stereo blend ).
  // OMODE Digital Output Mode (0=I2S, 6 = Left-justified, 8 = MSB at second DCLK after DFS pulse, 12 = MSB at first DCLK after DFS pulse).
  // OFALL Digital Output DCLK Edge (0 = use DCLK rising edge, 1 = use DCLK falling edge)
  _rx->digitalOutputFormat(0 /* OSIZE */, 0 /* OMONO */, 0 /* OMODE */, 0 /* OFALL*/);
#else
 _rx->setup(RESET_PIN, FM_FUNCTION);
#endif
}

Radio::~Radio() {}

char* Radio::getFreqName() {
  return freqName;
}

void Radio::off() {
  Serial.println("on");
  _rx->radioPowerUp();
}

void Radio::on() {
  Serial.println("off");
  _rx->powerDown();
}
//频率设置
void Radio::autoFrequencyDown() {
  //_rx->seekStationProgress(showFrequency,0);
  _rx->seekStationDown();
}

void Radio::autoFrequencyUp() {
  // _rx->seekStationProgress(showFrequency,1);
  _rx->seekStationUp();
}

void Radio::frequencyDown() {
  _rx->frequencyDown();
   

}

void Radio::frequencyUp() {
  _rx->frequencyUp();
  

}

void Radio::setFrequency(uint16_t val) {
  if(_isSSB){
    if(val>=10000){
      setSSB(USB);
    }else{
      setSSB(LSB);
    }
  }
  _rx->setFrequency(val);
}

uint16_t Radio::getFrequency() {
  return _rx->getCurrentFrequency();
}

//音量设置
void Radio::volumeUp() {
  _rx->volumeUp();
  volume = _rx->getVolume();
}

void Radio::volumeDown() {
  _rx->volumeDown();
  volume = _rx->getVolume();
}
uint16_t Radio::getVolume() {
  return volume;
}

void Radio::setVolume(uint16_t val) {
  _rx->setVolume(val >= 63 ? 63 : val);
  volume = _rx->getVolume();
}




char* Radio::change2(uint8_t c, void (*fn)()) {
  if (c == FM0) {
    change(0);
    fn();
  } else if (c ==  AM1) {
    change(1);
    fn();
  } else if (c == LW3) {
    change(2);
    fn();
  } else if (c == SW4) {
    change(3);
    fn();
  } else if (c == SW120) {
    change(4);
    fn();
  } else if (c == SW90) {
    change(5);
    fn();
  } else if (c == SW75) {
    change(6);
    fn();
  } else if (c == SW60) {
    change(7);
    fn();
  } else if (c == SW49) {
    change(8);
    fn();
  } else if (c == SW41) {
    change(9);
    fn();
  } else if (c == SW31) {
    change(10);
    fn();
  } else if (c == SW25) {
    change(11);
    fn();
  } else if (c == SW22) {
    change(12);
    fn();
  } else if (c == SW19) {
    change(13);
    fn();
  } else if (c == SW16) {
    change(14);
    fn();
  } else if (c == SW13) {
    change(15);
    fn();
  } else if (c == SW11) {
    change(16);
    fn();
  } else if (c == SSB18) {
    change(17);
    fn();
  }
  return freqName;
}
//切换模式
char* Radio::change(uint8_t index) {
  Band* b = &_band[index];
  Serial.println("FreqName:" + String(b->freqName) + " index:" + String(index));
  _fromFreq = b->minimumFreq;
  _toFreq = b->maximumFreq;
  _initialFreq = b->currentFreq;
  _step = b->currentStep;
  if (index == 0) {
    Serial.println("_fromFreq=>"+String(_fromFreq));
    Serial.println("_toFreq=>"+String(_toFreq));
    Serial.println("_initialFreq=>"+String(_initialFreq));
    Serial.println("_step=>"+String(_step));
#if defined(I2S_Radio)
  _rx->setup(RESET_PIN, -1, FM_CURRENT_MODE, SI473X_DIGITAL_AUDIO1, XOSCEN_RCLK); 
  _rx->digitalOutputSampleRate(48000);
// OSIZE Dgital Output Audio Sample Precision (0=16 bits, 1=20 bits, 2=24 bits, 3=8bits).
  // OMONO Digital Output Mono Mode (0=Use mono/stereo blend ).
  // OMODE Digital Output Mode (0=I2S, 6 = Left-justified, 8 = MSB at second DCLK after DFS pulse, 12 = MSB at first DCLK after DFS pulse).
  // OFALL Digital Output DCLK Edge (0 = use DCLK rising edge, 1 = use DCLK falling edge)
  _rx->digitalOutputFormat(0 /* OSIZE */, 0 /* OMONO */, 0 /* OMODE */, 0 /* OFALL*/);
#else
 //_rx->setup(RESET_PIN, FM_FUNCTION);
#endif
    _rx->setFM(_fromFreq, _toFreq, _initialFreq, _step);
    _rx->setSeekFmLimits(_fromFreq, _toFreq);
    _rx->setFrequency(_initialFreq);
    _isSSB = false;
    _isFM = true;
    _isAM = false;
  } else if (index >= 1 && index < BAND_SIZE - 1) {
    Serial.println("_fromFreq=>"+String(_fromFreq));
    Serial.println("_toFreq=>"+String(_toFreq));
    Serial.println("_initialFreq=>"+String(_initialFreq));
    Serial.println("_step=>"+String(_step));
    #if defined(I2S_Radio)
  _rx->setup(RESET_PIN, -1, AM_CURRENT_MODE, SI473X_DIGITAL_AUDIO1, XOSCEN_RCLK); 
  _rx->digitalOutputSampleRate(48000);
// OSIZE Dgital Output Audio Sample Precision (0=16 bits, 1=20 bits, 2=24 bits, 3=8bits).
  // OMONO Digital Output Mono Mode (0=Use mono/stereo blend ).
  // OMODE Digital Output Mode (0=I2S, 6 = Left-justified, 8 = MSB at second DCLK after DFS pulse, 12 = MSB at first DCLK after DFS pulse).
  // OFALL Digital Output DCLK Edge (0 = use DCLK rising edge, 1 = use DCLK falling edge)
  _rx->digitalOutputFormat(0 /* OSIZE */, 0 /* OMONO */, 0 /* OMODE */, 0 /* OFALL*/);
#else
// _rx->setup(RESET_PIN, AM_FUNCTION);
#endif
    _rx->setAM(_fromFreq, _toFreq, _initialFreq, _step);
    _rx->setSeekAmLimits(_fromFreq, _toFreq);
    _rx->setFrequency(_initialFreq);
    _isSSB = false;
    _isAM = true;
    _isFM = false;
  } else if(index == BAND_SIZE - 1 ){
    Serial.println("_fromFreq=>"+String(_fromFreq));
    Serial.println("_toFreq=>"+String(_toFreq));
    Serial.println("_initialFreq=>"+String(_initialFreq));
    Serial.println("_step=>"+String(_step));
#if defined(I2S_Radio)
  _rx->setup(RESET_PIN, -1, SSB_CURRENT_MODE, SI473X_DIGITAL_AUDIO1, XOSCEN_RCLK); 
  _rx->digitalOutputSampleRate(48000);
// OSIZE Dgital Output Audio Sample Precision (0=16 bits, 1=20 bits, 2=24 bits, 3=8bits).
  // OMONO Digital Output Mono Mode (0=Use mono/stereo blend ).
  // OMODE Digital Output Mode (0=I2S, 6 = Left-justified, 8 = MSB at second DCLK after DFS pulse, 12 = MSB at first DCLK after DFS pulse).
  // OFALL Digital Output DCLK Edge (0 = use DCLK rising edge, 1 = use DCLK falling edge)
  _rx->digitalOutputFormat(0 /* OSIZE */, 0 /* OMONO */, 0 /* OMODE */, 0 /* OFALL*/);
#else
// _rx->setup(RESET_PIN, AM_FUNCTION);
#endif
    _isSSB = true;
    _isFM = false;
    _isAM = false;
    uint16_t size_content = sizeof ssb_patch_content;
    uint16_t cmd_0x15_size = sizeof cmd_0x15;
    // loadSSB
    //_rx->setI2CFastModeCustom(500000);  // You can try rx.setI2CFastModeCustom(700000); or greater value
   //  _rx->loadPatch(ssb_patch_content, size_content, 1);
   // _rx->setI2CFastModeCustom(100000);
    //////////////////////////////////////////////////////
    /**压缩补丁**/
    _rx->loadCompressedPatch(ssb_patch_content, size_content, cmd_0x15, cmd_0x15_size);
    /////
    _rx->setTuneFrequencyAntennaCapacitor(1);
    _rx->setSSB(_fromFreq, _toFreq, _initialFreq, _step, _usbLsb);
    //BandWitdh
   // _rx->setSSBAudioBandwidth(2);
   // _rx->setSSBSidebandCutoffFilter(1);
    _rx->setSSBAutomaticVolumeControl(1);
    
  }else{
    b->freqName = "unkown";
  }
  Serial.println("FM:" + String(_isFM) + " AM:" + String(_isAM) + " ssb:" + String(_isSSB));
  _rx->setAvcAmMaxGain(90);
  _rx->setVolume(55);
  bandWidth(3);
  freqName = b->freqName;
  return b->freqName;
}

void Radio::setAvcAmMaxGain(uint8_t gain){
  _rx->setAvcAmMaxGain(gain);
}

uint8_t Radio::getAvcAmMaxGain(){
  return _rx->getCurrentAvcAmMaxGain();
}

bool Radio::isSSB() {
  return _isSSB;
}

bool Radio::isFM() {
  return _isFM;
}
bool Radio::isAM() {
  return _isAM;
}

//带宽设置
char* Radio::getBandWidth() {
  return currentBandWidth;
}

void Radio::bandWidth(int8_t bw) {
  if (_isSSB) {
    if (bw == 0) {
      ssb_bandwidthIdx = 0;
    }else{
    ssb_bandwidthIdx += bw;
    }
    if (ssb_bandwidthIdx > 5) {
      ssb_bandwidthIdx = 0;
    } else if (ssb_bandwidthIdx < 0) {
      ssb_bandwidthIdx = 5;
    }
    _rx->setSSBAudioBandwidth(ssb_bandwidthIdx);
    currentBandWidth = ssb_bandwidth[ssb_bandwidthIdx];
    Serial.print("SSB Filter - Bandwidth: ");
    Serial.print(String(currentBandWidth));
    Serial.println(" kHz index:"+String(ssb_bandwidthIdx));
    // If audio bandwidth selected is about 2 kHz or below, it is recommended to set Sideband Cutoff Filter to 0.
    if (ssb_bandwidthIdx == 0 || ssb_bandwidthIdx == 4 || ssb_bandwidthIdx == 5)
      _rx->setSSBSidebandCutoffFilter(0);
    else
      _rx->setSSBSidebandCutoffFilter(1);
  } else {
    if (!_rx->isCurrentTuneFM()) {
      if (bw == 0) {
        bandwidthIdx = 0;
      }else{
              bandwidthIdx += bw;
      }
      if (bandwidthIdx > 6) {
        bandwidthIdx = 0;
      } else if (bandwidthIdx < 0) {
        bandwidthIdx = 6;
      }
      _rx->setBandwidth(bandwidthIdx, 1);
      currentBandWidth = bandwidth[bandwidthIdx];
      Serial.print("Filter - Bandwidth: ");
      Serial.print(String(currentBandWidth));
      Serial.println(" kHz index:"+String(bandwidthIdx));
    }
  }
}

bool Radio::isCurrentTuneFM() {
  return _rx->isCurrentTuneFM();
}

char* Radio::rds() {
  _rx->rdsBeginQuery();  // gets RDS information
  char* utcTime = _rx->getRdsDateTime();
  char* stationName = _rx->getRdsStationName();
  char* programInfo = _rx->getRdsProgramInformation();
  char buffer[120];
  buffer[0] = '\n';  // New line control
  buffer[1] = '\0';  // end of string char array

  if (stationName != NULL) {
    strcat(buffer, "| Station Name: ");
    strcat(buffer, stationName);
  }
  if (programInfo != NULL) {
    strcat(buffer, " | Program information: ");
    strcat(buffer, programInfo);
  }

  if (utcTime != NULL) {
    strcat(buffer, " | Time (UTC): ");
    strcat(buffer, utcTime);
  }

  Serial.print("\ngetNumRdsFifoUsed: ");
  Serial.print(_rx->getNumRdsFifoUsed());
  Serial.print("\nSync: ");
  if (_rx->getNumRdsFifoUsed() > 0) {
    Serial.print(_rx->getRdsSync());
    if (strlen(buffer) > 4)
      Serial.print(buffer);
    return buffer;
  }
  return "";
}

void Radio::status() {
  _currentFreq = _rx->getCurrentFrequency();
  _rx->getCurrentReceivedSignalQuality();
  _rx->getAutomaticGainControl();
  String info = String(freqName) +" "+ String(_currentFreq ) + " " + ((_rx->getCurrentPilot()) ? " STEREO " : " MONO ");
  Serial.print(info + " | ");
  Serial.print((_rx->isAgcEnabled()) ? "AGC ON " : "AGC OFF");
  Serial.print(" | LNA GAIN index: ");
  Serial.print(_rx->getAgcGainIndex());
  Serial.print("/");
  // Serial.print(currentAGCAtt);
  Serial.print(" | BW :");
  // Serial.print(String(bandwidth[bandwidthIdx]));
  Serial.print("kHz");
  Serial.print(" | SNR: ");
  Serial.print(_rx->getCurrentSNR());
  Serial.print(" | RSSI: ");
  Serial.print(_rx->getCurrentRSSI());
  Serial.print(" dBuV");
  Serial.print(" | Volume: ");
  Serial.println(_rx->getVolume());
}

//信噪比
uint8_t Radio::getSnr(){
  return _rx->getCurrentSNR();
}

//信号强度
uint8_t Radio::getRssi(){
  return _rx->getCurrentRSSI();
}

void Radio::setSSB(uint8_t usblsb) {
  _usbLsb = usblsb;
  _rx->setSSB(_usbLsb);
}

uint8_t Radio::getSSB() {
  return _usbLsb;
}

void Radio::setAGC(uint8_t idx) {
  _agc = idx;
  if (idx <= 0) {
    _rx->setAutomaticGainControl(0, 0);
    _agc = 0;
  } else {
    if(_agc >= 26){
      _agc = 26;
      _rx->setAutomaticGainControl(1, _agc);
    }else{
    _rx->setAutomaticGainControl(1, _agc);
    }
  }
}

void Radio::setAGC(uint8_t dis,uint8_t idx) {
    _rx->setAutomaticGainControl(dis, idx);
}

uint8_t Radio::getAGC() {
  return _agc;
}

void Radio::setSSBBfo(int bfo) {
  Serial.println("set bfo:"+String(bfo));
  _bfo = bfo;
  if (_bfo == 0) {
    _rx->setSSBBfo(0);
  } else {
    _rx->setSSBBfo(_bfo);
  }
}

int Radio::getSSBBfo() {
  return _bfo;
}
